package com.yuntoyun.fwcore.util;
/*
 * +----------------------------------------------------------------------
 * | @Author: codeavatar   @Year：2021
 * +----------------------------------------------------------------------
 * | @Email: codeavatar@aliyun.com
 * +----------------------------------------------------------------------
 */
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Build;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission;

import static android.Manifest.permission.WRITE_SETTINGS;

/**
 * 屏幕工具类
 */
public class FwScreenUtil {

    private static FwScreenUtil init = null;

    public static FwScreenUtil builder() {
        if (null == init) {
            init = new FwScreenUtil();
        }
        return init;
    }

    /**
     * Return the width of screen, in pixel.
     *
     * @return the width of screen, in pixel
     */
    public int getScreenWidth() {
        WindowManager wm = (WindowManager) FwApplicationUtil.appContext().getSystemService
                (Context.WINDOW_SERVICE);
        if (wm == null) {
            return FwApplicationUtil.appContext().getResources().getDisplayMetrics().widthPixels;
        }
        Point point = new Point();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            wm.getDefaultDisplay().getRealSize(point);
        } else {
            wm.getDefaultDisplay().getSize(point);
        }
        return point.x;
    }

    /**
     * Return the height of screen, in pixel.
     *
     * @return the height of screen, in pixel
     */
    public int getScreenHeight() {
        WindowManager wm = (WindowManager) FwApplicationUtil.appContext().getSystemService
                (Context.WINDOW_SERVICE);
        if (wm == null) {
            return FwApplicationUtil.appContext().getResources().getDisplayMetrics().heightPixels;
        }
        Point point = new Point();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            wm.getDefaultDisplay().getRealSize(point);
        } else {
            wm.getDefaultDisplay().getSize(point);
        }
        return point.y;
    }

    /**
     * Return the density of screen.
     *
     * @return the density of screen
     */
    public float getScreenDensity() {
        return FwApplicationUtil.appContext().getResources().getDisplayMetrics().density;
    }

    /**
     * Return the screen density expressed as dots-per-inch.
     *
     * @return the screen density expressed as dots-per-inch
     */
    public int getScreenDensityDpi() {
        return FwApplicationUtil.appContext().getResources().getDisplayMetrics().densityDpi;
    }

    /**
     * Set full screen.
     *
     * @param activity The activity.
     */
    public void setFullScreen(@NonNull final Activity activity) {
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
    }

    /**
     * Set non full screen.
     *
     * @param activity The activity.
     */
    public void setNonFullScreen(@NonNull final Activity activity) {
        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
    }

    /**
     * Toggle full screen.
     *
     * @param activity The activity.
     */
    public void toggleFullScreen(@NonNull final Activity activity) {
        int fullScreenFlag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
        Window window = activity.getWindow();
        if ((window.getAttributes().flags & fullScreenFlag) == fullScreenFlag) {
            window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        } else {
            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        }
    }

    /**
     * Return whether screen is full.
     *
     * @param activity The activity.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public boolean isFullScreen(@NonNull final Activity activity) {
        int fullScreenFlag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
        return (activity.getWindow().getAttributes().flags & fullScreenFlag) == fullScreenFlag;
    }

    /**
     * Set the screen to landscape.
     *
     * @param activity The activity.
     */
    public void setLandscape(@NonNull final Activity activity) {
        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }

    /**
     * Set the screen to portrait.
     *
     * @param activity The activity.
     */
    public void setPortrait(@NonNull final Activity activity) {
        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    /**
     * Return whether screen is landscape.
     *
     * @return {@code true}: yes<br>{@code false}: no
     */
    public boolean isLandscape() {
        return FwApplicationUtil.appContext().getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE;
    }

    /**
     * Return whether screen is portrait.
     *
     * @return {@code true}: yes<br>{@code false}: no
     */
    public boolean isPortrait() {
        return FwApplicationUtil.appContext().getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_PORTRAIT;
    }

    /**
     * Return the rotation of screen.
     *
     * @param activity The activity.
     * @return the rotation of screen
     */
    public int getScreenRotation(@NonNull final Activity activity) {
        switch (activity.getWindowManager().getDefaultDisplay().getRotation()) {
            case Surface.ROTATION_0:
                return 0;
            case Surface.ROTATION_90:
                return 90;
            case Surface.ROTATION_180:
                return 180;
            case Surface.ROTATION_270:
                return 270;
            default:
                return 0;
        }
    }

    /**
     * Return the bitmap of screen.
     *
     * @param activity          The activity.
     * @param isDeleteStatusBar True to delete status bar, false otherwise.
     * @return the bitmap of screen
     */
    public Bitmap screenShot(@NonNull final Activity activity, boolean isDeleteStatusBar) {
        View decorView = activity.getWindow().getDecorView();
        decorView.setDrawingCacheEnabled(true);
        decorView.setWillNotCacheDrawing(false);
        Bitmap bmp = decorView.getDrawingCache();
        if (bmp == null) return null;
        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        Bitmap ret;
        if (isDeleteStatusBar) {
            Resources resources = activity.getResources();
            int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
            int statusBarHeight = resources.getDimensionPixelSize(resourceId);
            ret = Bitmap.createBitmap(
                    bmp,
                    0,
                    statusBarHeight,
                    dm.widthPixels,
                    dm.heightPixels - statusBarHeight
            );
        } else {
            ret = Bitmap.createBitmap(bmp, 0, 0, dm.widthPixels, dm.heightPixels);
        }
        decorView.destroyDrawingCache();
        return ret;
    }

    /**
     * Return whether screen is locked.
     *
     * @return {@code true}: yes<br>{@code false}: no
     */
    public boolean isScreenLock() {
        KeyguardManager km =
                (KeyguardManager) FwApplicationUtil.appContext().getSystemService(Context
                        .KEYGUARD_SERVICE);
        return km != null && km.inKeyguardRestrictedInputMode();
    }

    /**
     * Set the duration of sleep.
     * <p>Must hold {@code <uses-permission android:name="android.permission.WRITE_SETTINGS" />}</p>
     *
     * @param duration The duration.
     */
    @SuppressLint("SupportAnnotationUsage")
    @RequiresPermission(WRITE_SETTINGS)
    public void setSleepDuration(final int duration) {
        Settings.System.putInt(
                FwApplicationUtil.appContext().getContentResolver(),
                Settings.System.SCREEN_OFF_TIMEOUT,
                duration
        );
    }

    /**
     * Return the duration of sleep.
     *
     * @return the duration of sleep.
     */
    public int getSleepDuration() {
        try {
            return Settings.System.getInt(
                    FwApplicationUtil.appContext().getContentResolver(),
                    Settings.System.SCREEN_OFF_TIMEOUT
            );
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
            return -123;
        }
    }

    /**
     * Return whether device is tablet.
     *
     * @return {@code true}: yes<br>{@code false}: no
     */
    public boolean isTablet() {
        return (FwApplicationUtil.appContext().getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK)
                >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }

    /**
     * Return the size level of screen.
     *
     * @return Configuration.SCREENLAYOUT_SIZE_...
     */
    public String getScreenSizeLevel() {
        int size = (FwApplicationUtil.appContext().getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK);
        StringBuilder sb = new StringBuilder("");
        switch (size) {
            case Configuration.SCREENLAYOUT_SIZE_XLARGE:
                sb.append("xlarge");
                break;
            case Configuration.SCREENLAYOUT_SIZE_LARGE:
                sb.append("large");
                break;
            case Configuration.SCREENLAYOUT_SIZE_NORMAL:
                sb.append("normal");
                break;
            case Configuration.SCREENLAYOUT_SIZE_SMALL:
                sb.append("small");
                break;
            case Configuration.SCREENLAYOUT_SIZE_UNDEFINED:
                sb.append("undefined");
                break;
        }
        return sb.toString();
    }

    /**
     * Return the density level of screen.
     *
     * @param activity
     * @return
     */
    public String getScreenDensityLevel(@NonNull Activity activity) {
        DisplayMetrics metrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int density = metrics.densityDpi;

        StringBuilder sb = new StringBuilder("");
        switch (density) {
            case DisplayMetrics.DENSITY_XXXHIGH:
                sb.append("xxxhigh");
                break;
            case DisplayMetrics.DENSITY_XXHIGH:
                sb.append("xxhigh");
                break;
            case DisplayMetrics.DENSITY_XHIGH:
                sb.append("xhigh");
                break;
            case DisplayMetrics.DENSITY_HIGH:
                sb.append("high");
                break;
            case DisplayMetrics.DENSITY_MEDIUM:
                sb.append("medium");
                break;
            case DisplayMetrics.DENSITY_LOW:
                sb.append("low");
                break;
            case DisplayMetrics.DENSITY_TV:
                sb.append("tv");
                break;
        }
        return sb.toString();
    }

    /**
     * Adapt the screen for vertical slide.
     *
     * @param activity        The activity.
     * @param designWidthInPx The size of design diagram's width, in pixel.
     */
    public void adaptScreen4VerticalSlide(final Activity activity,
                                          final int designWidthInPx) {
        adaptScreen(activity, designWidthInPx, true);
    }

    /**
     * Adapt the screen for horizontal slide.
     *
     * @param activity         The activity.
     * @param designHeightInPx The size of design diagram's height, in pixel.
     */
    public void adaptScreen4HorizontalSlide(final Activity activity,
                                            final int designHeightInPx) {
        adaptScreen(activity, designHeightInPx, false);
    }

    /**
     * Reference from: https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA
     */
    private void adaptScreen(final Activity activity,
                             final int sizeInPx,
                             final boolean isVerticalSlide) {
        final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
        final DisplayMetrics appDm = FwApplicationUtil.appContext().getResources()
                .getDisplayMetrics();
        final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
        if (isVerticalSlide) {
            activityDm.density = activityDm.widthPixels / (float) sizeInPx;
        } else {
            activityDm.density = activityDm.heightPixels / (float) sizeInPx;
        }
        activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density);
        activityDm.densityDpi = (int) (160 * activityDm.density);

        appDm.density = activityDm.density;
        appDm.scaledDensity = activityDm.scaledDensity;
        appDm.densityDpi = activityDm.densityDpi;

        FwUtil.ADAPT_SCREEN_ARGS.sizeInPx = sizeInPx;
        FwUtil.ADAPT_SCREEN_ARGS.isVerticalSlide = isVerticalSlide;
    }

    /**
     * Cancel adapt the screen.
     *
     * @param activity The activity.
     */
    public void cancelAdaptScreen(final Activity activity) {
        final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
        final DisplayMetrics appDm = FwApplicationUtil.appContext().getResources()
                .getDisplayMetrics();
        final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
        activityDm.density = systemDm.density;
        activityDm.scaledDensity = systemDm.scaledDensity;
        activityDm.densityDpi = systemDm.densityDpi;

        appDm.density = systemDm.density;
        appDm.scaledDensity = systemDm.scaledDensity;
        appDm.densityDpi = systemDm.densityDpi;
    }

    /**
     * Cancel adapt the screen.
     */
    public void cancelAdaptScreen() {
        FwUtil.cancelAdaptScreen();
    }

    /**
     * Restore adapt the screen.
     * <p>U should call the method of {@link FwScreenUtil#adaptScreen4VerticalSlide(Activity, int)}
     * or {@link FwScreenUtil#adaptScreen4HorizontalSlide(Activity, int)} firstly.</p>
     */
    public void restoreAdaptScreen() {
        FwUtil.restoreAdaptScreen();
    }

    /**
     * Return whether adapt screen.
     *
     * @return {@code true}: yes<br>{@code false}: no
     */
    public boolean isAdaptScreen() {
        final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
        final DisplayMetrics appDm = FwApplicationUtil.appContext().getResources()
                .getDisplayMetrics();
        return systemDm.density != appDm.density;
    }
}
